iT邦幫忙

2023 iThome 鐵人賽

DAY 7
0
Modern Web

React 走出新手村 系列 第 7

React 走出新手村-深入useRef

  • 分享至 

  • xImage
  •  

ref講古

今天來聊聊 useRef 這個比較容易理解的 hook function,這期的內容,多半是我自己使用經驗的總結,也許不是那麼精確,但也多多少少能提供新手理解,那麼我們先從他的演進開始吧!

class component

在 class component 中 ref 是必須透過 createRef() 才能長出來給元件使用的東西。以我的理解來說,就是我長出一個dom的節點資料細節的監聽事件,但我不做細部updating的解析,取用時再去針對細部內容做比對。

import React, { Component } from 'react';

class InputField extends Component {
  constructor(props) {
    super(props);
    // 要先創ref
    this.inputRef = React.createRef();
  }

  focusInput = () => {
    this.inputRef.current.focus();
  };

  render() {
    return (
      <div>
        <input ref={this.inputRef} />
        <button onClick={this.focusInput}>Focus Input</button>
      </div>
    );
  }
}

export default InputField;

functional component

那麼在轉換成 functional component 之後就簡化成現在的 useRef 了,如範例:

import React, { useRef } from 'react';
// function component 的簡化
function InputField() {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
}

export default InputField;

ref 與 state 的差異

那麼,他與 useState 看似相同的功用到底有什麼好處呢?我下面舉個例子來讓大家複習 useRef 的好處:

import { useRef, useState } from "react";

// 在開始之前我先示範一個useState綁定input 範例
const RefExample = () => {
  // useState 的做法
  const [state, setState] = useState("");
  const onStateChange = (e) => {
    setState(e.target.value);
  };

  // useRef 的差異
  // 你可以想像它為useState的進階用法,
  // 用它來處理表單的好處就是能夠有效控制重複拿取state的問題,
  // 可以優化react的運行。
  const inputRef = useRef(null);

  // 當你打開以下兩個 console 的時候你就能理解為何會說它能優化的部分了
  // console.log("with state", state);
  // console.log("ref", inputRef);

  // 如果是用 state 綁定的話,所有的 setState 會促使 component 觸發 reredning
  // 但一樣的東西改用 useRef 來做的話,它就只是單純綁定在你設定的 html tag 之下,
  // 等到你需要觸發使用該值的時候,才會去更動 current 內的資料進行 rerending
  return (
    <div>
      <div>
        <h4>useRef 使用差異</h4>
      </div>
      <fieldset>
        <legend>用 useState 綁定</legend>
        <input type="text" value={state} onChange={onStateChange} />
        <button
          onClick={() => console.log(state)}
          style={{
            margin: "0 1rem"
          }}
        >
          check
        </button>
      </fieldset>
      <fieldset>
        <legend>用 useRef 綁定</legend>
        <input type="text" ref={inputRef} />
        <button
          onClick={() => console.log(inputRef.current?.value)}
          style={{
            margin: "0 1rem"
          }}
        >
          check
        </button>
      </fieldset>
    </div>
  );
};

export default RefExample;

https://ithelp.ithome.com.tw/upload/images/20230904/201290205YXbHkj8VN.jpg
這裡只要把我註解掉的 console 打開就可以知道其中的差異了,你會發現範例中使用 useState 綁定的 input 會因為 onChangesetState 導致觸發 state updating 造成重新渲染:
https://ithelp.ithome.com.tw/upload/images/20230904/20129020n7zYJymVuh.png

而相反的 useRef 的使用情況下就相對簡單,只需要設定完預設值之後,透過 ref 註冊在相對應下的 dom 節點,那麼在他底下的所有事件都能透過 current 去拿到,範例中的 value 就是透過已經套用 ref 註冊的 input 去提取它的 value,這樣的方式就不會像 useState 一樣觸發 updating 而造成重新渲染:
https://ithelp.ithome.com.tw/upload/images/20230904/20129020H7BhM9qnCW.png
這對減少重複性渲染確實很有幫助,尤其是在表單這類的使用情境中,我不需要在用戶尚未送出資料的時候一直重新渲染,算是一種比較容易理解的效能優化。

總結

那能不能將所有的 useStateuseRef 來取代呢?

答案也是否定的,要看你的使用情境是不是要一直追蹤使用者的操作,如果需要的話我相信 useState 會是比較好的選擇,如果不用的話那也許應該回頭看看 useRef 是否比較能勝任這類的需求。

以上就是今天的分享,下一篇會提及效能優化常提到的memo。

給全新手的大禮包

React基本Hook教學


上一篇
React 走出新手村-深入useEffect
下一篇
React 走出新手村-深入Memo
系列文
React 走出新手村 31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言